﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Text;
using dis.sys.hw1.ClientProxy;
using dis.sys.hw1.Contract;
using dis.sys.hw1.Model;

namespace dis.sys.hw1.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class HotelSearchService : ISearchService
    {
        private readonly ServiceLog _log = new ServiceLog(typeof(HotelSearchService), true);

        public IDictionary<string, DelegateHotelServiceProxy> HotelServiceProxies { get; set; }

        private IDictionary<Tuple<long, long, double, double>, Tuple<DateTime, IList<Hotel>>> queryCache;

        public IDictionary<Tuple<long, long, double, double>, Tuple<DateTime, IList<Hotel>>> QueryCache
        {
            get
            {
                return queryCache;
            }
            set
            {
                queryCache = value;
            }
        }

        public HotelSearchService(IDictionary<string, DelegateHotelServiceProxy> hotelServiceProxies = null,
            IDictionary<Tuple<long, long, double, double>, Tuple<DateTime, IList<Hotel>>> queryCache = null)
        {
            HotelServiceProxies = hotelServiceProxies ?? new ConcurrentDictionary<string, DelegateHotelServiceProxy>();
            QueryCache = queryCache;
        }

        public bool IsUp()
        {
            return true;
        }

        #region Implementation of ISearchService

        public IList<Hotel> Search(long fromDateTicks, long toDateTicks, double minStars, double minRating)
        {
            var fromDate = new DateTime(fromDateTicks);
            var toDate = new DateTime(toDateTicks);

            var hotels = new List<Hotel>();

            try
            {
                if (!HotelServiceProxies.Any())
                {
                    throw new FaultException("Failure, No available hotels please try again later");
                }

                foreach (var proxyEntry in HotelServiceProxies)
                {
                    var proxy = proxyEntry.Value;
                    try
                    {
                        hotels.AddRange(proxy.GetAvailableRooms(fromDate, toDate, minStars, minRating));
                    }
                    catch (Exception e)
                    {
                        _log.Error(proxyEntry.Key + " - " + e.Message);
                    }
                }
                return hotels;
            }
            catch (FaultException e)
            {
                if (WebOperationContext.Current != null)
                {
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
                    WebOperationContext.Current.OutgoingResponse.StatusDescription = e.Message;
                }
                return new List<Hotel>();
            }
        }

        private void SetHotelServiceUnavailableException(string hotelName)
        {
            if (WebOperationContext.Current != null)
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
                WebOperationContext.Current.OutgoingResponse.StatusDescription = "Failure, " + hotelName + " hotel server is down";
            }
        }

        private bool CheckHotelValidity(string hotelName)
        {
            if (!HotelServiceProxies.ContainsKey(hotelName))
            {
                if (WebOperationContext.Current != null)
                {
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
                    WebOperationContext.Current.OutgoingResponse.StatusDescription = "Failed, " + hotelName + " hotel is not registered";
                }
                return false;
            }
            return true;
        }

        #region old interface
        //public int Book(long fromDateTicks, long toDateTicks, int roomNumber, string hotelName)
        //{
        //    var fromDate = new DateTime(fromDateTicks);
        //    var toDate = new DateTime(toDateTicks);

        //    if (!CheckHotelValidity(hotelName)) return -1;

        //    try
        //    {
        //        return HotelServiceProxies[hotelName].BookRoom(fromDate, toDate, roomNumber);
        //    }
        //    catch (FaultException faultException)
        //    {
        //        if (WebOperationContext.Current != null)
        //            WebOperationContext.Current.OutgoingResponse.
        //                SetStatusAsNotFound(faultException.Message);
        //    }
        //    catch (Exception e)
        //    {
        //        SetHotelServiceUnavailableException(hotelName);
        //        _log.Error(hotelName + " - " + e.Message);
        //    }
        //    return -1;
        //}

        //public bool Cancel(int bookingId, string hotelName)
        //{
        //    if (!CheckHotelValidity(hotelName)) return false;

        //    try
        //    {
        //        return HotelServiceProxies[hotelName].Cancel(bookingId);
        //    }
        //    catch (FaultException faultException)
        //    {
        //        if (WebOperationContext.Current != null)
        //            WebOperationContext.Current.OutgoingResponse.
        //                SetStatusAsNotFound(faultException.Message);
        //    }
        //    catch (Exception e)
        //    {
        //        SetHotelServiceUnavailableException(hotelName);
        //        _log.Error(hotelName + " - " + e.Message);
        //    }
        //    return false;
        //}
        #endregion
        #endregion
    }
}
